﻿using System;
using System.Reflection;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Plugins;
using System.Collections.Generic;
using Visualizer.Windows;
using Plugins.Windows;
using System.Configuration;
using System.Xml;
using System.Windows.Media;
using System.Windows.Input;


namespace Visualizer
{
    /// <summary>
    /// Core class is the engine of Visualizer app
    /// </summary>
	public partial class Core: System.Windows.Application, ICore
    {
        #region Variables
        private enum EventType 
        {
            EVALUATE_BUTTON_CLICKED, 
            NEXT_BUTTON_CLICKED, 
            BACK_BUTTON_CLICKED, 
            FOCUS_CHANGED
        }

        public enum WindowButton
        {
            BackButton,
            NextButton,
            EvaluateButton
        }

        private HashSet<IPlugin> _plugins;
        private IPlugin _activePlugin;
        private MainWindow _mainWindow;

        private static Core _Instance;

        private string _fileName;

        private EventHandler<StateEventArgs> IPluginEventHandler;
        

        private static readonly string DEFAULT_FILE_FILTER = "All Files (*.*)|*.*";
        private static readonly string PLUGIN_TARGET_NAME = "Plugins.IPlugin";
        private static readonly string EXPRESSION_OK_TEXT = "Your expression is OK!";
        private static readonly string EXPRESSION_FAILS_TEXT = "Your expression is not correct!";
        private static readonly string FILE_OK_TEXT = "Selected file is OK!";
        private static readonly string FILE_FAILS_TEXT = "Selected file is not supported!";
        private static readonly string PLUGIN_FAILS_TEXT = "Application error - plugin failed !!!";

        private static readonly Brush _GreenBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF00FF00"));

        private Dictionary<string, string> _properties;

        #endregion

        /// <summary>
        /// Initialize instance
        /// </summary>
        private Core()
        {            
            _mainWindow = new MainWindow();
            this.MainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;

            // Handle events
            _mainWindow.OpenDialog.FileOk += new System.ComponentModel.CancelEventHandler(OnFileChoosed);
            _mainWindow.PluginSelectBox.SelectionChanged += new SelectionChangedEventHandler(OnPluginSelected);
            this.Startup += new StartupEventHandler(OnCoreStartup);

            // Create handlers            
            IPluginEventHandler = new EventHandler<StateEventArgs>(OnIPluginEvent);            

            _plugins = new HashSet<IPlugin>();            
            _properties = new Dictionary<string, string>();
            CreateProperties();

            this.MainWindow = _mainWindow;
            this.MainWindow.Show();
            this.MainWindow.Focus();
        }
        
        #region ICore Members

        /// <summary>
        /// Raise if plugin is changed
        /// </summary>
        public event EventHandler<FocusChangedArgs> FocusChanged;

        /// <summary>
        /// Raise if evaluate button is clicked
        /// </summary>
        public event EventHandler<ButtonClickedArgs> ButtonClicked;

        /// <summary>
        /// Raise if file is choosed
        /// </summary>
        public event EventHandler<FileChoosedArgs> FileChoosed;

        /// <summary>
        /// Gets RichTextBox used for expression writing
        /// </summary>
        public RichTextBox ExpressionBox
        {
            get { return _mainWindow.ExpressionTextBox; }
        }

        /// <summary>
        /// Gets or sets expression string writen in ExpressionBox
        /// </summary>
        public string Expression
        {
            get { return _mainWindow.ExpressionString; }
            set { _mainWindow.ExpressionString = value; }
        }

        /// <summary>
        /// Gets TreeListView used for showing XML tree
        /// </summary>
        public TreeListView View
        {
            get { return _mainWindow.View; }
        }

        /// <summary>
        /// Gets or set evaluate button content
        /// </summary>
        public string EvaluateButtonContent
        {
            get { return _mainWindow.EvaluateButtonContent; }
            set { _mainWindow.EvaluateButtonContent = value; }
        }

        public MenuItem PluginMenu
        {
            get { return _mainWindow.PluginMenu; }
        }

        public Dictionary<string, string> Settings
        {
            get { return _properties; }
        }

        #endregion      

        /// <summary>
        /// Delegate method for Application.Startup event
        /// </summary>
        /// <param name="sender">who raised event</param>
        /// <param name="e">event arguments</param>
        private void OnCoreStartup(object sender, EventArgs e)
        {
            Drive();
            _Instance = this;
        }
        
        /// <summary>
        /// Initialize required components. Loads plugins
        /// </summary>
        private void Drive()
        {
            _mainWindow = (MainWindow)this.MainWindow;
            LoadPlugins();
        }

        /// <summary>
        /// Gets current instance
        /// </summary>
        public static Core Instance
        {
            get { return _Instance; }
        }

        /// <summary>
        /// Loads and initialize plugins found in defined path
        /// </summary>
        private void LoadPlugins()
        {
            // Plugins path
            string path = System.IO.Directory.GetCurrentDirectory();
            string[] pluginFiles = Directory.GetFiles(path, "*.dll");

            // For each loaded file
            foreach (string pluginFile in pluginFiles)
            {
                try
                {
                    // try to crete assembly
                    Assembly pluginAssembly = Assembly.LoadFile(pluginFile);

                    if (pluginAssembly != null)
                    {
                        // for each type of assembly
                        foreach (Type pluginType in pluginAssembly.GetTypes())
                        {
                            // if is plugin
                            if (pluginType.GetInterface(PLUGIN_TARGET_NAME) != null)
                            {
                                // just create it
                                IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
                                plugin.Core = this;                                

                                _plugins.Add(plugin);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new Exception(e.StackTrace);
                }
            }
            
            // process loaded plugins
            ProcessPlugins();
        }

        /// <summary>
        /// Process loaded plugins - set status bar, set select box
        /// </summary>
        private void ProcessPlugins()
        {
            _mainWindow.BeginInit();
            
            _mainWindow.OpenDialog.Filter = DEFAULT_FILE_FILTER;

            foreach (IPlugin plugin in _plugins)
            {
                // Add to selection box plugin description
                _mainWindow.PluginSelectBox.Items.Add(plugin.ExpressionLanguageName);
            }

            // activate first plugin
            if (_plugins.Count > 0)
            {
                _mainWindow.PluginSelectBox.SelectedIndex = 0;
                _activePlugin = _plugins.ElementAt<IPlugin>(0);
                ActivatePlugin();
            }
            
            _mainWindow.View.Items.Clear();
            _mainWindow.EndInit();         
                      
        }        

        /// <summary>
        /// Process active plugin, do requeired for active plugin
        /// </summary>
        private void ActivatePlugin()
        {            
            if (_activePlugin != null)
            {
                // Register handlers
                _activePlugin.Event += IPluginEventHandler;

                // Set filter for open file dialog
                _mainWindow.OpenDialog.Filter = _activePlugin.FileFilter;

                _mainWindow.StatusText = "Expression Language: " + _activePlugin.ExpressionLanguageName;
                _activePlugin.HasFocus = true;                
            }
            else
            {
                _mainWindow.StatusText = "Expression Language: None";
                _mainWindow.StateCircle.Fill = _GreenBrush;
                _mainWindow.OpenDialog.Filter = DEFAULT_FILE_FILTER;
            }
        }

        /// <summary>
        /// Deactivates plugin, clear it
        /// </summary>
        private void DeactivatePlugin()
        {
            if (_activePlugin != null)
                // Removes handling for old active plugin
                _activePlugin.Event -= IPluginEventHandler;

            // Hides menu plugin creates
            HidePluginMenu();

            // Clear text box
            _mainWindow.ExpressionString = "";

            // Clear tree view
            _mainWindow.View.BeginInit();
            _mainWindow.View.Items.Clear();
            _mainWindow.View.EndInit();
        }

        /// <summary>
        /// Loads user config from file
        /// </summary>
        private void CreateProperties()
        {
            try
            {
                XmlDocument dom = new XmlDocument();
                dom.Load("Settings.xml");
                XmlNodeList nodeList = dom.SelectNodes("/settings/property");

                string key;
                string value;

                foreach (XmlNode node in nodeList)
                {
                    key = node.Attributes["key"].Value;
                    value = node.Attributes["value"].Value;
                    _properties.Add(key, value);
                }
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Reset all plugins focus. Set IPlugin.HasFocus = false
        /// </summary>
        private void ResetPluginsFocuses()
        {
            foreach (IPlugin plugin in _plugins)
                plugin.HasFocus = false;
        }

        /// <summary>
        /// Hides plugin menu item
        /// </summary>
        private void HidePluginMenu()
        {
            _mainWindow.PluginMenu.BeginInit();

            _mainWindow.PluginMenu.Header = "";
            _mainWindow.PluginMenu.Items.Clear();
            _mainWindow.PluginMenu.Header = "";
            _mainWindow.PluginMenu.Visibility = Visibility.Collapsed;

            _mainWindow.PluginMenu.EndInit();
        }

        #region Handling        

        /// <summary>
        /// Delegate method for FileOk event in OpenFileDialog. Method set fileName to active plugin and calls plugin's method Show()
        /// </summary>
        /// <param name="sender">object which raised event</param>
        /// <param name="e">event arguments</param>
        private void OnFileChoosed(object sender, EventArgs e)
        {
            _fileName = ((Microsoft.Win32.OpenFileDialog)sender).FileName;
            _mainWindow.StatusText = "File: " + _fileName;           
            if (FileChoosed != null)
                FileChoosed(this, new FileChoosedArgs(_fileName));
        }

        /// <summary>
        /// Delegate method for SelectionChanged event in PluginSelectBox. Handle active plugin - 
        /// set focus to active (selected) plugin
        /// </summary>
        /// <param name="sender">object which raised event</param>
        /// <param name="e">event arguments</param>
        private void OnPluginSelected(object sender, EventArgs e)
        {
            // Deactivates active plugin
            DeactivatePlugin();

            // Select new active plugin
            int index = ((System.Windows.Controls.ComboBox)sender).SelectedIndex;
            _activePlugin = index < 1 ? null : _plugins.ElementAt<IPlugin>(index - 1);

            OnFocusChanged();

            ActivatePlugin();            
        }

        /// <summary>
        /// Handle button click event
        /// </summary>
        /// <param name="type">button type which raised event</param>
        public void OnButtonClicked(WindowButton type)
        {
            ButtonClickedArgs.ButtonType buttonType = ButtonClickedArgs.ButtonType.EvaluateButton;
            switch (type)
            {
                case WindowButton.BackButton:
                    buttonType = ButtonClickedArgs.ButtonType.BackButton;
                    break;

                case WindowButton.NextButton:
                    buttonType = ButtonClickedArgs.ButtonType.NextButton;
                    break;

                case WindowButton.EvaluateButton:
                    buttonType = ButtonClickedArgs.ButtonType.EvaluateButton;
                    break;
            }

            if (ButtonClicked != null)
                ButtonClicked(this, new ButtonClickedArgs(buttonType));
        }
        
        /// <summary>
        /// Handle IPlugin.Event
        /// </summary>
        /// <param name="sender">IPlugin</param>
        /// <param name="e">StateEventArgs</param>
        protected void OnIPluginEvent(object sender, StateEventArgs e)
        {
            switch (e.State)
            {
                case StateEventArgs.PluginState.SHOWED:
                    OnIPluginShowed();
                    break;

                case StateEventArgs.PluginState.FILE_VALIDATION_OK:
                case StateEventArgs.PluginState.FILE_VALIDATION_FAILED:
                case StateEventArgs.PluginState.EXPRESSION_VALIDATION_OK:
                case StateEventArgs.PluginState.EXPRESSION_VALIDATION_FAILED:
                    OnIPluginValidation(e);
                    break;

                case StateEventArgs.PluginState.PROGRAM_FAILED:
                    OnIPluginFailed();
                    break;
            }
        }

        /// <summary>
        /// Handle IPlugin showed event
        /// </summary>        
        private void OnIPluginShowed() 
        {            
        }

        /// <summary>
        /// Handle IPlugin validation event
        /// </summary>        
        /// <param name="e">event args</param>
        private void OnIPluginValidation(StateEventArgs e) 
        {
            switch(e.State)
            {
                case StateEventArgs.PluginState.FILE_VALIDATION_OK: 
                    _mainWindow.StatusText = FILE_OK_TEXT;
                    _mainWindow.StateCircleColor = _GreenBrush;
                    break;

                case StateEventArgs.PluginState.EXPRESSION_VALIDATION_OK:
                    _mainWindow.StatusText = EXPRESSION_OK_TEXT;
                    _mainWindow.StateCircleColor = _GreenBrush;
                    break;

                case StateEventArgs.PluginState.FILE_VALIDATION_FAILED:
                    _mainWindow.StatusText = FILE_FAILS_TEXT;
                    _mainWindow.StateCircleColor = Brushes.Red;
                    break;

                case StateEventArgs.PluginState.EXPRESSION_VALIDATION_FAILED:
                    _mainWindow.StatusText = EXPRESSION_FAILS_TEXT;
                    _mainWindow.StateCircleColor = Brushes.Red;
                    break;
            }
        }

        /// <summary>
        /// Handle focus changed event
        /// </summary>
        private void OnFocusChanged()
        {
            if (FocusChanged != null)
                FocusChanged(this, new FocusChangedArgs(_activePlugin));
        }
        
        /// <summary>
        /// Handle IPlugin failed event
        /// </summary>
        private void OnIPluginFailed()
        {
            _mainWindow.StatusText = PLUGIN_FAILS_TEXT;
            _mainWindow.StateCircleColor = Brushes.Red;
        }              
        
        #endregion
    }
}
